// Client-server helpers for TCP/IP
// ClientInfo: wrapper for a socket which throws
//  Receive events, and allows for messaged communication (using
//  a specified character as end-of-message)
// Server: simple TCP server that throws Connect events
// ByteBuilder: utility class to manage byte arrays built up
//  in multiple transactions

// (C) Richard Smith 2005-8
//   bobjanova@gmail.com
// You can use this for free and give it to people as much as you like
// as long as you leave a credit to me :).

// Code to connect to a SOCKS proxy modified from
//   http://www.thecodeproject.com/csharp/ZaSocks5Proxy.asp

// Define this symbol to include console output in various places
//#define DEBUG

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Collections;
using System.Net.Sockets;
using System.Security.Cryptography;

using RedCorona.Cryptography;

[assembly:System.Reflection.AssemblyVersion("1.4.2008.0330")]

namespace RedCorona.Net {	
	public delegate void ConnectionRead(ClientInfo ci, String text);
	public delegate void ConnectionClosed(ClientInfo ci);
	public delegate void ConnectionReadBytes(ClientInfo ci, byte[] bytes, int len);
	public delegate void ConnectionReadMessage(ClientInfo ci, uint code, byte[] bytes, int len);
	public delegate void ConnectionReadPartialMessage(ClientInfo ci, uint code, byte[] bytes, int start, int read, int sofar, int totallength);
	public delegate void ConnectionNotify(ClientInfo ci);
	
	public enum ClientDirection {In, Out, Left, Right, Both};
	public enum MessageType {Unmessaged, EndMarker, Length, CodeAndLength};
	// ServerDES: The server sends an encryption key on connect
	// ServerRSAClientDES: The server sends an RSA public key, the client sends back a key
	public enum EncryptionType {None, ServerKey, ServerRSAClientKey};
	
	public class EncryptionUtils {
		static RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
		public static byte[] GetRandomBytes(int length, bool addByte){
			if(addByte && (length > 255)) throw new ArgumentException("Length must be 1 byte <256");
			byte[] random = new byte[length + (addByte ? 1 : 0)];
			rng.GetBytes(random);
			if(addByte) random[0] = (byte)length;
			return random;
		}
	}
	
	// OnReadBytes: catch the raw bytes as they arrive
	// OnRead: for text ended with a marker character
	// OnReadMessage: for binary info on a messaged client
	public class ClientInfo {
		internal Server server = null;
		private Socket sock;
		private String buffer;
		public event ConnectionRead OnRead;
		public event ConnectionClosed OnClose;
		public event ConnectionReadBytes OnReadBytes;
		public event ConnectionReadMessage OnReadMessage;
		public event ConnectionReadPartialMessage OnPartialMessage;
		public event ConnectionNotify OnReady;
		public MessageType MessageType;
		private ClientDirection dir;
		int id;
		bool alreadyclosed = false;
		public static int NextID = 100;
		//private ClientThread t;
		public object Data = null;
		
		// Encryption info
		EncryptionType encType;
		int encRead = 0, encStage, encExpected;
		internal bool encComplete;
		internal byte[] encKey;
		internal RSAParameters encParams;
		
		public EncryptionType EncryptionType {
			get { return encType; }
			set {
				if(encStage != 0) throw new ArgumentException("Key exchange has already begun"); 
				encType = value;
				encComplete = encType == EncryptionType.None;
				encExpected = -1;
			}
		}
		public bool EncryptionReady { get { return encComplete; } }
		internal ICryptoTransform encryptor, decryptor;
		public ICryptoTransform Encryptor { get { return encryptor; } }
		public ICryptoTransform Decryptor { get { return decryptor; } }
		
		private string delim;
		public const int BUFSIZE = 1024;
		byte[] buf = new byte[BUFSIZE];
		ByteBuilder bytes = new ByteBuilder(10);
		
		byte[] msgheader = new byte[8];
		byte headerread = 0;
		bool wantingChecksum = true;
		
		public string Delimiter {
			get { return delim; }
			set { delim = value; }
		}
		
		public ClientDirection Direction { get { return dir; } }
		public Socket Socket { get { return sock; } }
		public Server Server { get { return server; } }
		public int ID { get { return id; } }
		
		public bool Closed {
			get { return !sock.Connected; } 
		}
		
		public ClientInfo(Socket cl, bool StartNow) : this(cl, null, null, ClientDirection.Both, StartNow, EncryptionType.None) {}
		//public ClientInfo(Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d) : this(cl, read, readevt, d, true, EncryptionType.None) {}
		public ClientInfo(Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d, bool StartNow) : this(cl, read, readevt, d, StartNow, EncryptionType.None) {}
		public ClientInfo(Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d, bool StartNow, EncryptionType encryptionType){	
			sock = cl; buffer = ""; OnReadBytes = readevt; encType = encryptionType;
			encStage = 0; encComplete = encType == EncryptionType.None;
			OnRead = read;
			MessageType = MessageType.EndMarker;
			dir = d; delim = "\n";
			id = NextID; // Assign each client an application-unique ID
			unchecked{NextID++;}
			//t = new ClientThread(this);
			if(StartNow) BeginReceive();
		}
		
		public void BeginReceive(){
//			t.t.Start();
			sock.BeginReceive(buf, 0, BUFSIZE, 0, new AsyncCallback(ReadCallback), this);
		}
		
		public String Send(String text){
			byte[] ba = Encoding.UTF8.GetBytes(text);
			String s = "";
			for(int i = 0; i < ba.Length; i++) s += ba[i] + " ";
			Send(ba);
			return s;
		}
		
		public void SendMessage(uint code, byte[] bytes){ SendMessage(code, bytes, 0, bytes.Length); }
		public void SendMessage(uint code, byte[] bytes, byte paramType){ SendMessage(code, bytes, paramType, bytes.Length); }
		public void SendMessage(uint code, byte[] bytes, byte paramType, int len){
			if(paramType > 0){
				ByteBuilder b = new ByteBuilder(3);
				b.AddParameter(bytes, paramType);
				bytes = b.Read(0, b.Length);
				len = bytes.Length;
			} 
			
			lock(sock){
				byte checksum = 0; byte[] ba;
				switch(MessageType){
					case MessageType.CodeAndLength:					
						Send(ba = UintToBytes(code));
						for(int i = 0; i < 4; i++) checksum += ba[i];
						Send(ba = IntToBytes(len));
						for(int i = 0; i < 4; i++) checksum += ba[i];
						if(encType != EncryptionType.None) Send(new byte[]{checksum});
						break;
					case MessageType.Length:
						Send(ba = IntToBytes(len));
						for(int i = 0; i < 4; i++) checksum += ba[i];
						if(encType != EncryptionType.None) Send(new byte[]{checksum});
						break;
				}
				Send(bytes, len);
				if(encType != EncryptionType.None){
					checksum = 0;
					for(int i = 0; i < len; i++) checksum += bytes[i];
					Send(new byte[]{checksum});
				}
			}
			
		}
		public void Send(byte[] bytes){ Send(bytes, bytes.Length); }
		public void Send(byte[] bytes, int len){
			if(!encComplete) throw new IOException("Key exchange is not yet completed");
			if(encType != EncryptionType.None){
				byte[] outbytes = new byte[len];
				Encryptor.TransformBlock(bytes, 0, len, outbytes, 0);
				bytes = outbytes;
				//Console.Write("Encryptor IV: "); LogBytes(encryptor.Key, encryptor.IV.length);
			}
			#if DEBUG
			Console.Write(ID + " Sent: "); LogBytes(bytes, len);
			#endif
			sock.Send(bytes, len, SocketFlags.None);
		}
		
		public bool MessageWaiting(){
			FillBuffer(sock);
			return buffer.IndexOf(delim) >= 0;
		}
		
		public String Read(){
			//FillBuffer(sock);
			int p = buffer.IndexOf(delim);
			if(p >= 0){
				String res = buffer.Substring(0, p);
				buffer = buffer.Substring(p + delim.Length);
				return res;
			} else return "";
		}
		
		private void FillBuffer(Socket sock){
			byte[] buf = new byte[256];
			int read;
			while(sock.Available != 0){
				read = sock.Receive(buf);
				if(OnReadBytes != null) OnReadBytes(this, buf, read);
				buffer += Encoding.UTF8.GetString(buf, 0, read);
			}
		}
		
		void ReadCallback(IAsyncResult ar){
			try {
				int read = sock.EndReceive(ar);
				//Console.WriteLine("Socket "+ID+" read "+read+" bytes");
				if(read > 0){
					DoRead(buf, read);
					BeginReceive();
				} else {
					#if DEBUG
					Console.WriteLine(ID + " zero byte read closure");
					#endif
					Close();
				}
			} catch(SocketException e){
				#if DEBUG
				Console.WriteLine(ID + " socket exception closure: "+e);
				#endif
				Close();
			} catch(ObjectDisposedException){
				#if DEBUG
				Console.WriteLine(ID + " disposed exception closure");
				#endif
				Close();
			}
		}
		
		internal void DoRead(byte[] buf, int read){
			if(read > 0){				
				if(OnRead != null){ // Simple text mode
					buffer += Encoding.UTF8.GetString(buf, 0, read);
					while(buffer.IndexOf(delim) >= 0) OnRead(this, Read());
				}
			}
			ReadInternal(buf, read, false);
		}
		
		public static void LogBytes(byte[] buf, int len){
			byte[] ba = new byte[len];
			Array.Copy(buf, ba, len);
			Console.WriteLine(ByteBuilder.FormatParameter(new Parameter(ba, ParameterType.Byte)));		
		}
		
		void ReadInternal(byte[] buf, int read, bool alreadyEncrypted){			
			if((!alreadyEncrypted) && (encType != EncryptionType.None)){
				if(encComplete){
					#if DEBUG
					Console.Write(ID + " Received: "); LogBytes(buf, read);
					#endif
					buf = decryptor.TransformFinalBlock(buf, 0, read);
					#if DEBUG
					Console.Write(ID + " Decrypted: "); LogBytes(buf, read);
					#endif
				} else {
					// Client side key exchange
					int ofs = 0;
					if(encExpected < 0){ 
						encStage++;
						ofs++; read--; encExpected = buf[0]; // length of key to come
						encKey = new byte[encExpected];
						encRead = 0;
					}
					if(read >= encExpected){
						Array.Copy(buf, ofs, encKey, encRead, encExpected);
						int togo = read - encExpected;
						encExpected = -1;
						#if DEBUG
						Console.WriteLine(ID + " Read encryption key: "+ByteBuilder.FormatParameter(new Parameter(encKey, ParameterType.Byte)));
						#endif
						if(server == null) ClientEncryptionTransferComplete();
						else ServerEncryptionTransferComplete();
						if(togo > 0){
							byte[] newbuf = new byte[togo];
							Array.Copy(buf, read + ofs - togo, newbuf, 0, togo);
							ReadInternal(newbuf, togo, false);
						}
					} else {
						Array.Copy(buf, ofs, encKey, encRead, read);
						encExpected -= read; encRead += read;					
					}
					return;
				}
			}
			
			if((!alreadyEncrypted) && (OnReadBytes != null)) OnReadBytes(this, buf, read);
			
			if((OnReadMessage != null) && (MessageType != MessageType.Unmessaged)){
				// Messaged mode
				int copied;
				uint code = 0;
				switch(MessageType){
					case MessageType.CodeAndLength:
					case MessageType.Length:
						int length;
						if(MessageType == MessageType.Length){
							copied = FillHeader(ref buf, 4, read);						
							if(headerread < 4) break;
							length = GetInt(msgheader, 0, 4);
						} else{
							copied = FillHeader(ref buf, 8, read);
							if(headerread < 8) break;
							code = (uint)GetInt(msgheader, 0, 4);
							length = GetInt(msgheader, 4, 4);
						}
						if(read == copied) break;
						// If encryption is on, the next byte is a checksum of the header
						int ofs = 0;
						if(wantingChecksum && (encType != EncryptionType.None)){
							byte checksum = buf[0];
							ofs++;
							wantingChecksum = false;
							byte headersum = 0;
							for(int i = 0; i < 8; i++) headersum += msgheader[i];
							if(checksum != headersum){
								Close();
								throw new IOException("Header checksum failed! (was "+checksum+", calculated "+headersum+")");
							}
						}
						bytes.Add(buf, ofs, read - ofs - copied);
						if(encType != EncryptionType.None) length++; // checksum byte
						
						// Now we know we are reading into the body of the message
						#if DEBUG
						Console.WriteLine(ID + " Added "+(read - ofs - copied)+" bytes, have "+bytes.Length+" of "+length);
						#endif
						if(OnPartialMessage != null) OnPartialMessage(this, code, buf, ofs, read - ofs - copied, bytes.Length, length);
						
						if(bytes.Length >= length){
							// A message was received!
							 headerread = 0; wantingChecksum = true;
							byte[] msg = bytes.Read(0, length);
							if(encType != EncryptionType.None){
								byte checksum = msg[length - 1], msgsum = 0;
								for(int i = 0; i < length - 1; i++) msgsum += msg[i];
								if(checksum != msgsum){
									Close();
									throw new IOException("Content checksum failed! (was "+checksum+", calculated "+msgsum+")");
								}
								OnReadMessage(this, code, msg, length - 1);
							} else OnReadMessage(this, code, msg, length);
							// Don't forget to put the rest through the mill
							int togo = bytes.Length - length;
							if(togo > 0){
								byte[] whatsleft = bytes.Read(length, togo);
								bytes.Clear();
								ReadInternal(whatsleft, whatsleft.Length, true);
							} else bytes.Clear();
						}
						//if(OnStatus != null) OnStatus(this, bytes.Length, length);
						break;
				}
			}
		}
		
		int FillHeader(ref byte[] buf, int to, int read) {
			int copied = 0;
			if(headerread < to){
				// First copy the header into the header variable.
				for(int i = 0; (i < read) && (headerread < to); i++, headerread++, copied++){
					msgheader[headerread] = buf[i];
				}
			}
			if(copied > 0){
				// Take the header bytes off the 'message' section
				byte[] newbuf = new byte[read - copied];
				for(int i = 0; i < newbuf.Length; i++) newbuf[i] = buf[i + copied];
				buf = newbuf;
			}
			return copied;
		}
		
		internal ICryptoTransform MakeEncryptor(){ return MakeCrypto(true); }
		internal ICryptoTransform MakeDecryptor(){ return MakeCrypto(false); }
		internal ICryptoTransform MakeCrypto(bool encrypt){
			if(encrypt) return new SimpleEncryptor(encKey);
			else return new SimpleDecryptor(encKey);
		}		
		
		void ServerEncryptionTransferComplete(){
			switch(encType){
				case EncryptionType.None:
					throw new ArgumentException("Should not have key exchange for unencrypted connection!");				
				case EncryptionType.ServerKey:
					throw new ArgumentException("Should not have server-side key exchange for server keyed connection!");				
				case EncryptionType.ServerRSAClientKey:
					// Symmetric key is in RSA-encoded encKey
					RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
					rsa.ImportParameters(encParams);
					encKey = rsa.Decrypt(encKey, false);
					#if DEBUG
					Console.WriteLine("Symmetric key is: "); LogBytes(encKey, encKey.Length);
					#endif
					MakeEncoders();
					server.KeyExchangeComplete(this);
					break;
			}
		}
		
		void ClientEncryptionTransferComplete(){
			// A part of the key exchange process has been completed, and the key is
			// in encKey
			switch(encType){
				case EncryptionType.None:
					throw new ArgumentException("Should not have key exchange for unencrypted connection!");				
				case EncryptionType.ServerKey:
					// key for transfer is now in encKey, so all is good
					MakeEncoders();
					break;
				case EncryptionType.ServerRSAClientKey:
					// Stage 1: modulus; Stage 2: exponent
					// When the exponent arrives, create a random DES key
					// and send it
					switch(encStage){
						case 1: encParams.Modulus = encKey; break;
						case 2:
							encParams.Exponent = encKey;
							RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
							rsa.ImportParameters(encParams);
							encKey = EncryptionUtils.GetRandomBytes(24, false);
							byte[] send = GetLengthEncodedVector(rsa.Encrypt(encKey, false));
							sock.Send(send);							
							MakeEncoders();
							break;
					}
					break;
			}
		}
		
		internal void MakeEncoders(){
			encryptor = MakeEncryptor();	
			decryptor = MakeDecryptor();
			if(OnReady != null) OnReady(this);
			encComplete = true;
		}
			
		public static byte[] GetLengthEncodedVector(byte[] from){
			int l = from.Length;
			if(l > 255) throw new ArgumentException("Cannot length encode more than 255");
			byte[] to = new byte[l + 1];
			to[0] = (byte)l;
			Array.Copy(from, 0, to, 1, l);
			return to;
		}
		
		public static int GetInt(byte[] ba, int from, int len){
			int r = 0;
			for(int i = 0; i < len; i++)
				r += ba[from + i] << ((len - i - 1) * 8);
			return r;
		}
		
		public static int[] GetIntArray(byte[] ba){ return GetIntArray(ba, 0, ba.Length); }
		public static int[] GetIntArray(byte[] ba, int from, int len){
			int[] res = new int[len / 4];
			for(int i = 0; i < res.Length; i++){
				res[i] = GetInt(ba, from + (i * 4), 4);
			}
			return res;
		}
		
		public static uint[] GetUintArray(byte[] ba){
			uint[] res = new uint[ba.Length / 4];
			for(int i = 0; i < res.Length; i++){
				res[i] = (uint)GetInt(ba, i * 4, 4);
			}
			return res;
		}
		
		public static byte[] IntToBytes(int val){ return UintToBytes((uint)val); }
		public static byte[] UintToBytes(uint val){
			byte[] res = new byte[4];
			for(int i = 3; i >= 0; i--){
				res[i] = (byte)val; val >>= 8;
			}
			return res;
		}
		
		public static byte[] IntArrayToBytes(int[] val){
			byte[] res = new byte[val.Length * 4];
			for(int i = 0; i < val.Length; i++){
				byte[] vb = IntToBytes(val[i]);
				res[(i * 4)] = vb[0];
				res[(i * 4) + 1] = vb[1];
				res[(i * 4) + 2] = vb[2];
				res[(i * 4) + 3] = vb[3];
			}
			return res;
		}
		
		public static byte[] UintArrayToBytes(uint[] val){
			byte[] res = new byte[val.Length * 4];
			for(uint i = 0; i < val.Length; i++){
				byte[] vb = IntToBytes((int)val[i]);
				res[(i * 4)] = vb[0];
				res[(i * 4) + 1] = vb[1];
				res[(i * 4) + 2] = vb[2];
				res[(i * 4) + 3] = vb[3];
			}
			return res;
		}
		
		public static byte[] StringArrayToBytes(string[] val, Encoding e){
			byte[][] baa = new byte[val.Length][];
			int l = 0;
			for(int i = 0; i < val.Length; i++){ baa[i] = e.GetBytes(val[i]); l += 4 + baa[i].Length; }
			byte[] r = new byte[l + 4];
			IntToBytes(val.Length).CopyTo(r, 0);
			int ofs = 4;
			for(int i = 0; i < baa.Length; i++){
				IntToBytes(baa[i].Length).CopyTo(r, ofs); ofs += 4;
				baa[i].CopyTo(r, ofs); ofs += baa[i].Length;
			}
			return r;
		}
		
		public static string[] GetStringArray(byte[] ba, Encoding e){
			int l = GetInt(ba, 0, 4), ofs = 4;
			string[] r = new string[l];
			for(int i = 0; i < l; i++){
				int thislen = GetInt(ba, ofs, 4); ofs += 4;
				r[i] = e.GetString(ba, ofs, thislen); ofs += thislen;
			}
			return r;
		}
		
		public void Close(){
			if(!alreadyclosed){
				if(server != null) server.ClientClosed(this);
				if(OnClose != null) OnClose(this);
				alreadyclosed = true;
				#if DEBUG
				Console.WriteLine("**closed client** at "+DateTime.Now.Ticks);
				#endif
			}
			sock.Close();
		}
	}
	
	public class Sockets {
		// Socks proxy inspired by http://www.thecodeproject.com/csharp/ZaSocks5Proxy.asp
		public static SocksProxy SocksProxy;
		public static bool UseSocks = false;
		
		public static Socket CreateTCPSocket(String address, int port){return CreateTCPSocket(address, port, UseSocks, SocksProxy);}
		public static Socket CreateTCPSocket(String address, int port, bool useSocks, SocksProxy proxy){
			Socket sock;
			if(useSocks) sock = ConnectToSocksProxy(proxy.host, proxy.port, address, port, proxy.username, proxy.password);
			else {
				IPAddress host = Dns.GetHostEntry(address).AddressList[0];
				sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
				sock.Connect(new IPEndPoint(host, port));
			}
			return sock;
		}

		private static string[] errorMsgs=	{
			"Operation completed successfully.",
			"General SOCKS server failure.",
			"Connection not allowed by ruleset.",
			"Network unreachable.",
			"Host unreachable.",
			"Connection refused.",
			"TTL expired.",
			"Command not supported.",
			"Address type not supported.",
			"Unknown error."
		};

		public static Socket ConnectToSocksProxy(IPAddress proxyIP, int proxyPort, String destAddress, int destPort, string userName, string password){
			byte[] request = new byte[257];
			byte[] response = new byte[257];
			ushort nIndex;
			
			IPAddress destIP = null;
			
			try{ destIP = IPAddress.Parse(destAddress); }
			catch { }
			
			IPEndPoint proxyEndPoint = new IPEndPoint(proxyIP, proxyPort);

			// open a TCP connection to SOCKS server...
			Socket s;
			s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
			s.Connect(proxyEndPoint);	
/*			} catch(SocketException){
				throw new SocketException(0, "Could not connect to proxy server.");
			}*/

			nIndex = 0;
			request[nIndex++]=0x05; // Version 5.
			request[nIndex++]=0x02; // 2 Authentication methods are in packet...
			request[nIndex++]=0x00; // NO AUTHENTICATION REQUIRED
			request[nIndex++]=0x02; // USERNAME/PASSWORD
			// Send the authentication negotiation request...
			s.Send(request,nIndex,SocketFlags.None);

			// Receive 2 byte response...
			int nGot = s.Receive(response,2,SocketFlags.None);	
			if (nGot!=2)
				throw new ConnectionException("Bad response received from proxy server.");

			if (response[1]==0xFF)
			{	// No authentication method was accepted close the socket.
				s.Close();
				throw new ConnectionException("None of the authentication method was accepted by proxy server.");
			}

			byte[] rawBytes;

			if (/*response[1]==0x02*/true)
			{//Username/Password Authentication protocol
				nIndex = 0;
				request[nIndex++]=0x05; // Version 5.

				// add user name
				request[nIndex++]=(byte)userName.Length;
				rawBytes = Encoding.Default.GetBytes(userName);
				rawBytes.CopyTo(request,nIndex);
				nIndex+=(ushort)rawBytes.Length;

				// add password
				request[nIndex++]=(byte)password.Length;
				rawBytes = Encoding.Default.GetBytes(password);
				rawBytes.CopyTo(request,nIndex);
				nIndex+=(ushort)rawBytes.Length;

				// Send the Username/Password request
				s.Send(request,nIndex,SocketFlags.None);
				// Receive 2 byte response...
				nGot = s.Receive(response,2,SocketFlags.None);	
				if (nGot!=2)
					throw new ConnectionException("Bad response received from proxy server.");
				if (response[1] != 0x00)
					throw new ConnectionException("Bad Usernaem/Password.");
			}
			// This version only supports connect command. 
			// UDP and Bind are not supported.

			// Send connect request now...
			nIndex = 0;
			request[nIndex++]=0x05;	// version 5.
			request[nIndex++]=0x01;	// command = connect.
			request[nIndex++]=0x00;	// Reserve = must be 0x00

			if (destIP != null)
			{// Destination adress in an IP.
				switch(destIP.AddressFamily)
				{
					case AddressFamily.InterNetwork:
						// Address is IPV4 format
						request[nIndex++]=0x01;
						rawBytes = destIP.GetAddressBytes();
						rawBytes.CopyTo(request,nIndex);
						nIndex+=(ushort)rawBytes.Length;
						break;
					case AddressFamily.InterNetworkV6:
						// Address is IPV6 format
						request[nIndex++]=0x04;
						rawBytes = destIP.GetAddressBytes();
						rawBytes.CopyTo(request,nIndex);
						nIndex+=(ushort)rawBytes.Length;
						break;
				}
			}
			else
			{// Dest. address is domain name.
				request[nIndex++]=0x03;	// Address is full-qualified domain name.
				request[nIndex++]=Convert.ToByte(destAddress.Length); // length of address.
				rawBytes = Encoding.Default.GetBytes(destAddress);
				rawBytes.CopyTo(request,nIndex);
				nIndex+=(ushort)rawBytes.Length;
			}

			// using big-edian byte order
			byte[] portBytes = BitConverter.GetBytes((ushort)destPort);
			for (int i=portBytes.Length-1;i>=0;i--)
				request[nIndex++]=portBytes[i];

			// send connect request.
			s.Send(request,nIndex,SocketFlags.None);
			s.Receive(response);	// Get variable length response...
			if (response[1]!=0x00)
				throw new ConnectionException(errorMsgs[response[1]]);
			// Success Connected...
			return s;
		}
	}
	
	public struct SocksProxy {
		public IPAddress host;
		public ushort port;
		public string username, password;
		
		public SocksProxy(String hostname, ushort port, String username, String password){
			this.port = port;
			host = Dns.GetHostEntry(hostname).AddressList[0];
			this.username = username; this.password = password;
		}
	}
	
	public class ConnectionException: Exception {
		public ConnectionException(string message) : base(message) {}
	}
	
	// Server code cribbed from Framework Help
	public delegate bool ClientEvent(Server serv, ClientInfo new_client); // whether to accept the client
	public class Server {
		class ClientState {
			// To hold the state information about a client between transactions
			internal Socket Socket = null;
			internal const int BufferSize = 1024;
			internal byte[] buffer = new byte[BufferSize];
			internal StringBuilder sofar = new StringBuilder();  

			internal ClientState(Socket sock){
				Socket = sock;
			}
		}
  
  ArrayList clients = new ArrayList();
  Socket ss;
  
  public event ClientEvent Connect, ClientReady;
  public IEnumerable Clients {
  	get { return clients; }
  }
  
  public Socket ServerSocket {
  	get { return ss; }
  }
  
  public ClientInfo this[int id]{
  	get {
  		foreach(ClientInfo ci in Clients)
  			if(ci.ID == id) return ci;
  		return null;
  	}
  }

		private EncryptionType encType;
		public EncryptionType DefaultEncryptionType {
			get { return encType; }
			set { encType = value; }
		}
  
  public int Port {
  	get { return ((IPEndPoint)ss.LocalEndPoint).Port; }
  }
  
  public Server(int port) : this(port, null) {}
  public Server(int port, ClientEvent connDel) {
  	Connect = connDel;
  	
  	ss = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
  	ss.Bind(new IPEndPoint(IPAddress.Any, port));
   ss.Listen(100);
   
   // Start the accept process. When a connection is accepted, the callback
   // must do this again to accept another connection
   ss.BeginAccept(new AsyncCallback(AcceptCallback), ss);
  }
  
  internal void ClientClosed(ClientInfo ci){
  	clients.Remove(ci);
  }
  
  public void Broadcast(byte[] bytes){
  	foreach(ClientInfo ci in clients) ci.Send(bytes);
  }
  
  public void BroadcastMessage(uint code, byte[] bytes){BroadcastMessage(code, bytes, 0); }
  public void BroadcastMessage(uint code, byte[] bytes, byte paramType){
  	foreach(ClientInfo ci in clients) ci.SendMessage(code, bytes, paramType);
  }
  
  // ASYNC CALLBACK CODE
		void AcceptCallback(IAsyncResult ar) {
			try{
				Socket server = (Socket) ar.AsyncState;
				Socket cs = server.EndAccept(ar);

				// Start the thing listening again
				server.BeginAccept(new AsyncCallback(AcceptCallback), server);

				ClientInfo c = new ClientInfo(cs, null, null, ClientDirection.Both, false);
				c.server = this;
				// Allow the new client to be rejected by the application
				if(Connect != null){
					if(!Connect(this, c)){
						// Rejected
						cs.Close();
						return;
					}
				}
				// Initiate key exchange
				c.EncryptionType = encType;
				switch(encType){
					case EncryptionType.None: KeyExchangeComplete(c); break;
					case EncryptionType.ServerKey:
						c.encKey = GetSymmetricKey();
						byte[] key = ClientInfo.GetLengthEncodedVector(c.encKey);
						cs.Send(key);
						c.MakeEncoders();
						KeyExchangeComplete(c); 
						break;
					case EncryptionType.ServerRSAClientKey:
						RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
						RSAParameters p = rsa.ExportParameters(true);
						cs.Send(ClientInfo.GetLengthEncodedVector(p.Modulus));
						cs.Send(ClientInfo.GetLengthEncodedVector(p.Exponent));
						c.encParams = p;
						break;
					default: throw new ArgumentException("Unknown or unsupported encryption type "+encType);
				}
				clients.Add(c);
				c.BeginReceive();
			} catch(ObjectDisposedException) {  }
			catch(SocketException) { }
			catch(Exception e) { Console.WriteLine(e); }			
		}
		
		protected virtual byte[] GetSymmetricKey(){
			return EncryptionUtils.GetRandomBytes(24, false);
		}
		
		internal void KeyExchangeComplete(ClientInfo ci){
			// Key exchange is complete on this client. Client ready
			// handlers may still force a close of the connection
			if(ClientReady != null)
				if(!ClientReady(this, ci)) ci.Close();
		}
		
		~Server() { Close(); }
		public void Close(){
			ArrayList cl2 = new ArrayList();
			foreach(ClientInfo c in clients) cl2.Add(c);
			foreach(ClientInfo c in cl2) c.Close();
			
			ss.Close();
		}
	}
	
	public class ByteBuilder : MarshalByRefObject {
		byte[][] data;
		int packsize, used;
		
		public int Length {
			get {
				int len = 0;
				for(int i = 0; i < used; i++) len += data[i].Length;
				return len;
			}
		}
		
		public byte this[int i]{
			get { return Read(i, 1)[0]; }
		}
		
		public ByteBuilder() : this(10) {}
		public ByteBuilder(int packsize){
			this.packsize = packsize; used = 0;
			data = new byte[packsize][];
		}
		
		public ByteBuilder(byte[] data) {
			packsize = 1;
			used = 1;
			this.data = new byte[][] { data };
		}
		
		public ByteBuilder(byte[] data, int len) : this(data, 0, len) {}
		public ByteBuilder(byte[] data, int from, int len) : this(1) {
			Add(data, from, len);
		}
		public void Add(byte[] moredata){ Add(moredata, 0, moredata.Length); }
		public void Add(byte[] moredata, int from, int len){
//Console.WriteLine("Getting "+from+" to "+(from+len-1)+" of "+moredata.Length);
			if(used < packsize){
				data[used] = new byte[len];
				for(int j = from; j < from + len; j++)
					data[used][j - from] = moredata[j];
				used++;
			} else {
				// Compress the existing items into the first array
				byte[] newdata = new byte[Length + len];
				int np = 0;
				for(int i = 0; i < used; i++)
					for(int j = 0; j < data[i].Length; j++)
						newdata[np++] = data[i][j];
				for(int j = from; j < from + len; j++)
					newdata[np++] = moredata[j];
				data[0] = newdata;
				for(int i = 1; i < used; i++) data[i] = null;
				used = 1;
			}
		}
		
		public byte[] Read(int from, int len){
			if(len == 0) return new byte[0];
			byte[] res = new byte[len];
			int done = 0, start = 0;
			
			for(int i = 0; i < used; i++){
				if((start + data[i].Length) <= from){
					start += data[i].Length; continue;
				}
				// Now we're in the data block
				for(int j = 0; j < data[i].Length; j++){
					if((j + start) < from) continue;
					res[done++] = data[i][j];
					if(done == len) return res;
				}
			}
			
			throw new ArgumentException("Datapoints "+from+" and "+(from+len)+" must be less than "+Length);
		}
		
		public void Clear(){
			used = 0;
			for(int i = 0; i < used; i++) data[i] = null;			
		}
		
		public Parameter GetParameter(ref int index){
			Parameter res = new Parameter();
			res.Type = Read(index++, 1)[0];
			byte[] lenba = Read(index, 4);
			index += 4;
			int len = ClientInfo.GetInt(lenba, 0, 4);
			res.content = Read(index, len);
			index += len;
			return res;
		}
		
		public void AddParameter(Parameter param){ AddParameter(param.content, param.Type); }
		public void AddParameter(byte[] content, byte Type){
			Add(new byte[]{Type});
			Add(ClientInfo.IntToBytes(content.Length));
			Add(content);
		}
		
		public static String FormatParameter(Parameter p){
			switch(p.Type){
				case ParameterType.Int:
					int[] ia = ClientInfo.GetIntArray(p.content);
					StringBuilder sb = new StringBuilder();
					foreach(int i in ia) sb.Append(i + " ");
					return sb.ToString();
				case ParameterType.Uint:
					ia = ClientInfo.GetIntArray(p.content);
					sb = new StringBuilder();
					foreach(int i in ia) sb.Append(i.ToString("X8") + " ");
					return sb.ToString();
				case ParameterType.String:
					return Encoding.UTF8.GetString(p.content);
				case ParameterType.StringArray:
					string[] sa = ClientInfo.GetStringArray(p.content, Encoding.UTF8);
					sb = new StringBuilder();
					foreach(string s in sa) sb.Append(s + "; ");
					return sb.ToString();
				case ParameterType.Byte:
					sb = new StringBuilder();
					foreach(int b in p.content) sb.Append(b.ToString("X2") + " ");
					return sb.ToString();
				default: return "??";
			}
		}		
	}
	
	[Serializable]
	public struct Parameter {
		public byte Type;
		public byte[] content;
		
		public Parameter(byte[] content, byte type){
			this.content = content; Type = type;
		}
	}
	
	public struct ParameterType {
		public const byte Unparameterised = 0;
		public const byte Int = 1;
		public const byte Uint = 2;
		public const byte String = 3;
		public const byte Byte = 4;
		public const byte StringArray = 5;
	}
}

namespace RedCorona.Cryptography {
	// Cryptographic classes
	public abstract class BaseCrypto : ICryptoTransform {
		public int InputBlockSize { get { return 1; } }
		public int OutputBlockSize { get { return 1; } }
		public bool CanTransformMultipleBlocks { get { return true; } }
		public bool CanReuseTransform { get { return true; } }		
		
		protected byte[] key;
		protected byte currentKey;
		protected int done = 0, keyinx = 0;
		
		protected BaseCrypto(byte[] key){
			if(key.Length == 0) throw new ArgumentException("Must provide a key");
			this.key = key;
			currentKey = 0;
			for(int i = 0; i < key.Length; i++) currentKey += key[i];
		}
		
		protected abstract byte DoByte(byte b);
		public int TransformBlock(byte[] from, int frominx, int len, byte[] to, int toinx){
			for(int i = 0; i < len; i++){			
				byte oldkey = currentKey;
				to[toinx + i] = DoByte(from[frominx + i]);
				#if DEBUG
//				Console.WriteLine("  encrypting "+from[frominx + i]+" to "+to[toinx + i]+", key is "+oldkey);
				#endif
				BumpKey();
			}
			return len;
		}
		public byte[] TransformFinalBlock(byte[] from, int frominx, int len){
			byte[] to = new byte[len];
			TransformBlock(from, frominx, len, to, 0);
			return to;
		}
		protected void BumpKey(){
			keyinx = (keyinx + 1) % key.Length;
			currentKey = Multiply257(key[keyinx], currentKey);
		}
		
		protected static byte Multiply257(byte a, byte b){
		 	return (byte)((((a + 1) * (b + 1)) % 257) - 1);
		}
		
		protected static byte[] complements = {0,128,85,192,102,42,146,224,199,179,186,149,177,201,119,240,120,99,229,89,48,221,189,74,71,88,237,100,194,59,198,248,147,188,234,49,131,114,144,44,162,152,5,110,39,94,174,165,20,35,125,172,96,118,242,178,247,225,60,29,58,227,101,252,86,73,233,222,148,245,180,24,168,65,23,185,246,200,243,150,164,209,95,204,126,2,64,183,25,19,208,175,151,215,45,82,52,138,134,17,27,62,4,214,163,176,244,187,223,249,43,217,115,123,37,112,133,158,53,14,16,157,139,113,219,50,84,254,1,171,205,36,142,116,98,239,241,202,97,122,143,218,132,140,38,212,6,32,68,11,79,92,41,251,193,228,238,121,117,203,173,210,40,104,80,47,236,230,72,191,253,129,51,160,46,91,105,12,55,9,70,232,190,87,231,75,10,107,33,22,182,169,3,154,28,197,226,195,30,8,77,13,137,159,83,130,220,235,90,81,161,216,145,250,103,93,211,111,141,124,206,21,67,108,7,57,196,61,155,18,167,184,181,66,34,207,166,26,156,135,15,136,54,78,106,69,76,56,31,109,213,153,63,170,127,255};
		protected static byte Complement257(byte b){ return complements[b]; }
		
		public void Dispose(){} // for IDisposable
	}
	
	public class SimpleEncryptor : BaseCrypto {
		public SimpleEncryptor(byte[] key) : base(key) {}
		
		protected override byte DoByte(byte b){
			byte b2 = Multiply257((byte)(b+currentKey), currentKey);
			currentKey = Multiply257((byte)(b+b2), currentKey);
			return b2;
		}
	}
	public class SimpleDecryptor : BaseCrypto {
		public SimpleDecryptor(byte[] key) : base(key) {}
		
		protected override byte DoByte(byte b){
			byte b2 = (byte)(Multiply257(b, Complement257(currentKey)) - currentKey);
			currentKey = Multiply257((byte)(b+b2), currentKey);
			return b2;
		}
	}
}